package com.fcscs.cservice.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.jasperreports.engine.JRException;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.fcscs.core.common.CallResult;
import com.fcscs.core.common.ErrorEnums;
import com.fcscs.core.dao.UserDao;
import com.fcscs.core.domain.UserBase;
import com.fcscs.core.service.FcsManager;
import com.fcscs.cservice.common.CServiceCommonEnums.FolioStatus;
import com.fcscs.cservice.common.CServiceCommonEnums.NotifType;
import com.fcscs.cservice.common.CServiceCommonEnums.RCStatus;
import com.fcscs.cservice.common.CServiceCommonEnums.RsvReqMsgEnum;
import com.fcscs.cservice.common.CServiceCommonEnums.TemplateType;
import com.fcscs.cservice.common.CServiceCommonUtils.NotifEntityIntf;
import com.fcscs.cservice.common.CServiceConstants;
import com.fcscs.cservice.dao.CServiceCommonDao;
import com.fcscs.cservice.dao.GuestProfileDao;
import com.fcscs.cservice.dao.ReservationDao;
import com.fcscs.cservice.domain.Folio;
import com.fcscs.cservice.domain.Message;
import com.fcscs.cservice.domain.NotifDestination;
import com.fcscs.cservice.domain.NotifTemplate;
import com.fcscs.cservice.domain.PropertyConfiguration;
import com.fcscs.cservice.domain.RC;
import com.fcscs.cservice.domain.ReservationBase;
import com.fcscs.cservice.domain.model.CheckInConfiguration;
import com.fcscs.cservice.domain.model.CheckOutConfiguration;
import com.fcscs.cservice.domain.model.FolioView;
import com.fcscs.cservice.domain.model.FolioViewForFile;
import com.fcscs.cservice.domain.model.GuestProfileView;
import com.fcscs.cservice.domain.model.MessageIdAndStatus;
import com.fcscs.cservice.domain.model.MessageView;
import com.fcscs.cservice.domain.model.RCView;
import com.fcscs.cservice.domain.model.RCViewForFile;
import com.fcscs.cservice.domain.model.ReservationConf;
import com.fcscs.cservice.domain.model.ReservationView;
import com.fcscs.cservice.domain.validator.CServiceCommonValidators;
import com.fcscs.cservice.domain.validator.ReservationValidators;
import com.fcscs.cservice.service.CServiceCommonManager;
import com.fcscs.cservice.service.GuestProfileManager;
import com.fcscs.cservice.service.ReportGeneratorManager;
import com.fcscs.cservice.service.ReservationManager;
import com.fcscs.share.domain.nonexport.FolioAttachment;
import com.fcscs.share.domain.nonexport.RCAttachment;
import com.fcscs.share.service.MiscDBManager;

@Service("reservationManager")
public class ReservationManagerImpl extends FcsManager implements ReservationManager {

	@Autowired
	private ReservationDao rsvDao;
	@Autowired
	private GuestProfileDao gpDao;
	@Autowired
	private GuestProfileManager gpMgr;
	@Autowired
	private UserDao userDao;
	@Autowired
	private CServiceCommonDao ccDao;
	@Autowired
	private CServiceCommonManager ccMgr;
	@Autowired
	private MiscDBManager miscMgr;
	@Autowired
	private ReportGeneratorManager reportGen;

	private static Logger LOGGER = Logger.getLogger(ReservationManagerImpl.class);

	@Override
	public List<ReservationView> findReservations(long propId, String term, Long rsvStatusId, Date startTime, Date endTime,
			int start, int resultCnt) {
		List<ReservationView> rvs = rsvDao.findReservations(propId, term, rsvStatusId, startTime, endTime, start, resultCnt);
		loadReservationDetail(rvs);
		return rvs;
	}

	@Override
	public int findReservationsCnt(long propId, String term, Long rsvStatusId, Date startTime, Date endTime) {
		return rsvDao.findReservationsCnt(propId, term, rsvStatusId, startTime, endTime);
	}

	@Override
	public List<ReservationView> findReservations(long propId, String firstName, String lastName, Long rsvStatus, String roomNo,
			String memberNo, Long roomStatus, String baggageNo, Date arrivDate, Date departDate, Date eta, Date etd,
			Date startTime, Date endTime, int start, int resultCnt) {
		List<ReservationView> rvs = rsvDao.findReservations(propId, firstName, lastName, rsvStatus, roomNo, memberNo,
				roomStatus, baggageNo, arrivDate, departDate, eta, etd, startTime, endTime, start, resultCnt);
		loadReservationDetail(rvs);
		return rvs;
	}

	@Override
	public int findReservationsCnt(long propId, String firstName, String lastName, Long rsvStatus, String roomNo, String memberNo,
			Long roomStatus, String baggageNo, Date arrivDate, Date departDate, Date eta, Date etd, Date startTime, Date endTime) {
		return rsvDao.findReservationsCnt(propId, firstName, lastName, rsvStatus, roomNo, memberNo, roomStatus, baggageNo,
				arrivDate, departDate, eta, etd, startTime, endTime);
	}

	@Override
	public List<ReservationView> findRsvForFolio(long propId, String firstName, String lastName, Long rsvStatusId, String roomNo, Date arrivStart,
			Date arrivEnd, Date departStart, Date departEnd, Date date, int start, int resultCnt) {
		return rsvDao.findRsvForFolio(propId, firstName, lastName, rsvStatusId, roomNo, arrivStart, arrivEnd, departStart, departEnd,
				date, start, resultCnt);
	}

	@Override
	public int findRsvForFolioCnt(long propId, String firstName, String lastName, Long rsvStatusId, String roomNo, Date arrivStart,
			Date arrivEnd, Date departStart, Date departEnd, Date date) {
		return rsvDao.findRsvForFolioCnt(propId, firstName, lastName, rsvStatusId, roomNo, arrivStart, arrivEnd, departStart, departEnd, date);
	}

	@Override
	public ReservationView getReservationView(long id) {
		ReservationView rv = rsvDao.getReservationView(id);
		if (rv != null) {
			List<ReservationView> rvs = new ArrayList<ReservationView>(1);
			rvs.add(rv);
			loadReservationDetail(rvs);
		}
		return rv;
	}

	@Override
	public ReservationView getCurReservationViewByGP(long gpId) {
		ReservationView rv = rsvDao.getCurReservationViewByGP(gpId);
//		if (rv != null) {
//			List<ReservationView> rvs = new ArrayList<ReservationView>(1);
//			rvs.add(rv);
//			loadReservationDetail(rvs);
//		}
		return rv;
	}

	private void loadReservationDetail(List<ReservationView> rsvViews) {
		if (rsvViews == null) {
			return;
		}
		Set<Long> gpIds = new HashSet<Long>();
		Set<Long> rsvIds = new HashSet<Long>();
		for (ReservationView v : rsvViews) {
			gpIds.add(v.getGuestProfileId());
			rsvIds.add(v.getReservationViewId());
		}
		
		Map<Long, List<ReservationView.Membership>> memMap = gpDao.getMemberships(gpIds);
		Map<Long, List<ReservationView.RC>> rcMap = rsvDao.getRCs(rsvIds);
		for (ReservationView v : rsvViews) {
			List<ReservationView.Membership> memList = (List<ReservationView.Membership>)memMap.get(v.getGuestProfileId());
			if (memList != null) {
				v.setMemberships(memList);
			}
			List<ReservationView.RC> rcList = (List<ReservationView.RC>) rcMap.get(v.getReservationViewId());
			if (rcList != null) {
				v.setRcs(rcList);
			}
		}
	}

	@Override
	public RCView getRCView(long id, String userName) {
		RCView rcv = rsvDao.getRCView(id);
		if (rcv != null) {
			rcv.setGuestProfile(gpMgr.getGuestProfileView(rcv.getGuestProfileId()));
			rcv.setRates(rsvDao.getRateViews(id));
			rcv.setGsaCode(userName);
		}
		return rcv;
	}

	@Override
	public RCViewForFile getRCViewForFile(long id) {
		RCViewForFile rcv = rsvDao.getRCViewForFile(id);
		if (rcv != null) {
			rcv.setGuestProfile(gpMgr.getGuestProfileViewForFile(rcv.getGuestProfileId()));
			rcv.setRates(rsvDao.getRateViewsForFile(id));
			UserBase u = userDao.getUser(UserBase.class, rcv.getUpdatedBy());
			if (u != null) {
				rcv.setGsaCode(u.getName());
			}
		}
		return rcv;
	}

	ReservationBase updateReservation(ReservationBase rsv) {
		ErrorEnums err = new ReservationValidators.ReservationValidator().validate(rsv, rsv.getId() == null);
		if (err != null) {
			LOGGER.debug(err);
			return null;
		}
		transferBaseVals(rsv, rsv, rsv.isNew());
		return rsvDao.updateReservation(rsv);
	}

	RC updateRC(RC rc) {
		ErrorEnums err = new ReservationValidators.RCValidator().validate(rc, rc.getId() == null);
		if (err != null) {
			LOGGER.debug(err);
			return null;
		}
		transferBaseVals(rc, rc, rc.isNew());
		return rsvDao.updateRC(rc);
	}

	@Override
	public RCView updateRCView(RCView rcView, byte[] initial, byte[] signature) {

		RC rc = rsvDao.getRC(rcView.getRcViewId());
		if (RCStatus.COMPLETE.equals(rc.getStatusEnum())) {
			return rcView;
		}

		rc = transRCFromRCView(rcView, rc);
		if (initial != null && signature != null) {
			RCAttachment att = new RCAttachment();
			att.setRcId(rc.getId());
			att.setOrgId(rc.getOrgId());
			att.setPropertyId(rc.getPropertyId());
			att.setInitial(initial);
			att.setSignature(signature);
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("initial", new ByteArrayInputStream(initial));
			parameters.put("signature", new ByteArrayInputStream(signature));
			byte[] rcPDF = null;
			try {
				rcPDF = reportGen.getRCPDF(rcView, parameters);				
			} catch (IOException e) {
				e.printStackTrace();
			} catch (JRException e) {
				e.printStackTrace();
			}
			att.setRcPDF(rcPDF);
			CallResult<RCAttachment> result = miscMgr.createRCAttachment(att);
			if (result.hasError()) {
				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				return null;
			}
			rc.setGuestSignatureDateTime(new Date());
			rc.setStatusEnum(RCStatus.COMPLETE);
		}

		rc = updateRC(rc);
		if (rc == null) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			LOGGER.error("update rc " + rcView.getRcViewId() + " failed!");
			return null;
		}
		rcView = transRCViewFromRC(rc, rcView);

		ReservationBase rsv = rsvDao.getReservation(rcView.getReservationId());
		rsv = transRsvFromRCView(rcView, rsv);
		rsv = updateReservation(rsv);
		if (rsv == null) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			LOGGER.error("update reservation " + rcView.getReservationId() + " failed!");
			return null;
		}
		rcView = transRCViewFromRsv(rsv, rcView);

		GuestProfileView gp = rcView.getGuestProfile();
		if (gp != null) {
			gp.setOrgId(rcView.getOrgId());
			gp.setPropertyId(rcView.getPropertyId());
			gp = gpMgr.updateGuestProfileView(rcView.getGuestProfile());
		}
		rcView.setGuestProfile(gp);
		return rcView;
	}

	private RCView transRCViewFromRC(RC rc, RCView rcView) {
		rcView.setPaymentMethodId(rc.getPaymentMethodId());
		rcView.setPaymentComment(rc.getPaymentComment());
		rcView.setShareReservation(rc.getShareReservation());
		rcView.setExpressCheckout(rc.getExpressCheckout());
		rcView.setRcRemarks(rc.getRcRemarks());

		rcView.setUpdated(rc.getUpdated());
		rcView.setDeposite(rc.getDeposite());
		rcView.setGuestSignatureDateTime(rc.getGuestSignatureDateTime());
		rcView.setGuarantee(rc.getGuarantee());
		rcView.setDirectBillCodeId(rc.getDirectBillCodeId());
		rcView.setPaymentMethodId(rc.getPaymentMethodId());
		rcView.setPaymentComment(rc.getPaymentComment());
		rcView.setStatus(rc.getStatus());
		rcView.setBillingInstruction(rc.getBillingInstruction());
		rcView.setSpecialService(rc.getSpecialService());
		return rcView;
	}

	private RC transRCFromRCView(RCView rcView, RC rc) {
		rc.setPaymentMethodId(rcView.getPaymentMethodId());
		rc.setPaymentComment(rcView.getPaymentComment());
		rc.setShareReservation(rcView.isShareReservation());
		rc.setExpressCheckout(rcView.getExpressCheckout());
		rc.setRcRemarks(rcView.getRcRemarks());
		return rc;
	}

	private RCView transRCViewFromRsv(ReservationBase rsv, RCView rcView) {
		rcView.setArrivalDateTime(rsv.getArrivalDateTime());
		rcView.setDepartureDateTime(rsv.getDepartureDateTime());
		rcView.setNumberOfGuest(rsv.getNumberOfGuest());
		return rcView;
	}

	private ReservationBase transRsvFromRCView(RCView rcView, ReservationBase rsv) {
		rsv.setArrivalDateTime(rcView.getArrivalDateTime());
		rsv.setDepartureDateTime(rcView.getDepartureDateTime());
		rsv.setNumberOfGuest(rcView.getNumberOfGuest());
		return rsv;
	}

	@Override
	public FolioView getFolioView(long id) {
		FolioView fv = rsvDao.getFolioView(id);
		if (fv != null) {
			fv.setBillings(rsvDao.getBillings(fv.getFolioId()));
			fv.setVipCodes(gpDao.getVIPCodesString(fv.getGuestProfileId()));
		}
		return fv;
	}

	@Override
	public FolioViewForFile getFolioViewForFile(long id) {
		FolioViewForFile fv = rsvDao.getFolioViewForFile(id);
		if (fv != null) {
			fv.setBillings(rsvDao.getBillingsForFile(fv.getFolioId()));
			fv.setVipCodes(gpDao.getVIPCodesString(fv.getGuestProfileId()));
			fv.setCurrency(ccDao.getCurrency(fv.getPropertyId()));
		}
		return fv;
	}

	@Override
	public List<MessageView> getMessages(long rsvId) {
		return rsvDao.getMessages(rsvId);
	}

	@Override
	public List<MessageIdAndStatus> updateMessages(List<MessageIdAndStatus> messageList) {
		if (messageList == null || messageList.size() <= 0) {
			return messageList;
		}
		Set<Long> ids = new HashSet<Long>(messageList.size());
		Map<Long, MessageIdAndStatus> map = new HashMap<Long, MessageIdAndStatus>();
		for (MessageIdAndStatus msg: messageList) {
			ids.add(msg.getMessageId());
			map.put(msg.getMessageId(), msg);
		}
		List<Message> msgList = rsvDao.getMessages(ids);
		if (msgList == null) {
			return null;
		}
		for (Message m: msgList) {
			MessageIdAndStatus msg = map.get(m.getId());
			if (msg != null) {
				m.setIsRead(msg.getIsRead());
				transferBaseVals(m, m, false);
			}
		}
		msgList = rsvDao.updateMessages(msgList);
		if (msgList == null) {
			return null;
		}
		messageList.clear();
		for (Message m: msgList) {
			MessageIdAndStatus msg = map.get(m.getId());
			if (msg != null) {
				msg.setIsRead(m.getIsRead());
				messageList.add(msg);
			}
		}
		return messageList;
	}

	@Override
	public boolean expressCheckIn(long rsvId, RCView rc) {
		ReservationView rsv = getReservationView(rsvId);
		if (rsv == null) {
			return false;
		}
		//TODO
		boolean sendReqResult = ccMgr.sendRequest(rsv.getOrgId(), rsv.getPropertyId(), NotifType.CI_REQ, TemplateType.CI_REQ, null, getRsvNotifEntity(rsv));
		if (!sendReqResult) {
			LOGGER.debug("send checkIn request error");
		}
		return true;
	}

	@Override
	public boolean checkOut(boolean isAuto, long folioId, byte[] signature, long paymentMethodId, String remarks) {
		Folio folio = rsvDao.getFolio(folioId);
		
		if (FolioStatus.COMPLETE.equals(folio.getStatusEnum())) {
			return false;
		}
		if (signature == null || signature.length == 0) {
			return false;
		}
		try {
			FolioAttachment att = new FolioAttachment();
			att.setOrgId(folio.getOrgId());
			att.setPropertyId(folio.getPropertyId());
			att.setSignature(signature);
			att.setFolioId(folio.getId());
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("signature", new ByteArrayInputStream(signature));
			att.setFolioPDF(reportGen.getFolioPDF(folio, parameters));
			CallResult<FolioAttachment> res = miscMgr.createFolioAttachment(att);
			if (res.hasError()) {
				return false;
			}
			folio.setGuestSignatureDateTime(new Date());
			folio.setStatusEnum(FolioStatus.COMPLETE);
			folio.setPaymethodId(paymentMethodId);
			folio.setRemarks(remarks);
			transferBaseVals(folio, folio, false);
			rsvDao.updateFolio(folio);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		ReservationView rsv = getReservationView(folio.getReservationId());
		if (rsv == null) {
			return false;
		}
		//TODO
		boolean sendReqResult = ccMgr.sendRequest(folio.getOrgId(), folio.getPropertyId(), NotifType.CO_REQ, isAuto ? TemplateType.CO_AUTO_REQ : TemplateType.CO_MANU_REQ,
				null, getRsvNotifEntity(rsv));
		if (!sendReqResult) {
			LOGGER.debug("send checkOut request error");
		}
		return true;
	}

	@Override
	public boolean sendEFolio(long rsvId, long folioId) {
		ReservationView rsv = getReservationView(rsvId);
		if (rsv == null) {
			return false;
		}
		//TODO
		boolean sendReqResult = ccMgr.sendRequest(rsv.getOrgId(), rsv.getPropertyId(), NotifType.BILL_NOTIF, TemplateType.BILL_NOTIF, null, getRsvNotifEntity(rsv));
		if (!sendReqResult) {
			LOGGER.debug("send eFolio request error");
		}
		return true;
	}

	@Override
	public ReservationConf getRsvConf(long orgId, long propId) {
		return ccDao.getRsvConf(orgId, propId);
	}

	@Override
	public ReservationConf saveRsvConf(ReservationConf conf) {
		PropertyConfiguration update;
		if (conf.getId() != null) {
			update = ccDao.getPropertyConf(conf.getId());
		} else {
			update = new PropertyConfiguration();
			update.setOrgId(conf.getOrgId());
			update.setPropertyId(conf.getPropertyId());
		}
		update.setBookingSearchDays(conf.getBookingSearchDays());
		update.setReservationSearchDays(conf.getReservationSearchDays());
		update.setGsa(conf.getGsa());
		update.setGsaSecLang(conf.getGsaSecLang());
		update.setGsaThdLang(conf.getGsaSecLang());
		update.setSpg(conf.getSpg());
		update.setSpgSecLang(conf.getSpgSecLang());
		update.setSpgThdLang(conf.getSpgThdLang());
		update.setTandc(conf.getTandc());
		update.setTandcSecLang(conf.getTandcSecLang());
		ErrorEnums err = new CServiceCommonValidators.PropertyConfValidator().validate(update, update.isNew());
		if (err != null) {
			LOGGER.debug(err);
			return null;
		}
		if (ccDao.isPropertyConfTaken(conf.getOrgId(), conf.getPropertyId(), conf.getId())) {
			LOGGER.debug(conf.getPropertyId() + " " + ErrorEnums.GeneralError.UNIQUE_CONS);
		}
		transferBaseVals(update, update, update.isNew());
		ccDao.savePropertyConf(update);
		conf.setId(update.getId());
		conf.setBookingSearchDays(update.getBookingSearchDays());
		conf.setReservationSearchDays(update.getReservationSearchDays());
		conf.setGsa(update.getGsa());
		conf.setGsaSecLang(update.getGsaSecLang());
		conf.setGsaThdLang(update.getGsaSecLang());
		conf.setSpg(update.getSpg());
		conf.setSpgSecLang(update.getSpgSecLang());
		conf.setSpgThdLang(update.getSpgThdLang());
		conf.setTandc(update.getTandc());
		conf.setTandcSecLang(update.getTandcSecLang());
		return conf;
	}

	
	private NotifDestination searchNotifDest(List<NotifDestination> list, NotifType type) {
		if (list == null || list.size() <= 0 || type == null) {
			return null;
		}
		int index = NotifDestination.binarySearch(list, type);
		if (index >= 0) {
			return list.get(index);
		}
		return null;
	}
	
	private NotifTemplate searchNotifTmpl(List<NotifTemplate> list, TemplateType type) {
		if (list == null || list.size() <= 0 || type == null) {
			return null;
		}
		int index = NotifTemplate.binarySearch(list, type);
		if (index >= 0) {
			return list.get(index);
		}
		return null;
	}

	public CheckInConfiguration getCheckInConf(long orgId, long propId) {
		CheckInConfiguration ciConf = ccDao.getCheckInConf(orgId, propId);
		if (ciConf == null) {
			ciConf = new CheckInConfiguration();
			ciConf.setOrgId(orgId);
			ciConf.setPropertyId(propId);
			ciConf.setReqTimeout(CServiceConstants.DEFAULT_CI_TO);
		}
		List<NotifDestination> ciNotifs = ccDao.getNotifDestinations(orgId, propId, new NotifType[]{NotifType.CI_REQ, NotifType.CI_TIMEOUT_ALERT});
		if (ciNotifs != null) {
			Collections.sort(ciNotifs, NotifDestination.getComparator());
			ciConf.setReqDest(searchNotifDest(ciNotifs, NotifType.CI_REQ));
			ciConf.setToAlertDest(searchNotifDest(ciNotifs, NotifType.CI_TIMEOUT_ALERT));
		}
		List<NotifTemplate> ciTmpls = ccDao.getNotifTemplates(orgId, propId, new TemplateType[]{TemplateType.CI_REQ, TemplateType.CI_TIMEOUT_ALERT});
		if (ciTmpls != null) {
			Collections.sort(ciTmpls, NotifTemplate.getComparator());
			ciConf.setReqTmpl(searchNotifTmpl(ciTmpls, TemplateType.CI_REQ));
			ciConf.setToAlertTmpl(searchNotifTmpl(ciTmpls, TemplateType.CI_TIMEOUT_ALERT));
		}
		return ciConf;
	}
	
	public CheckOutConfiguration getCheckOutConf(long orgId, long propId) {
		CheckOutConfiguration coConf = ccDao.getCheckOutConf(orgId, propId);
		if (coConf == null) {
			coConf = new CheckOutConfiguration();
			coConf.setOrgId(orgId);
			coConf.setPropertyId(propId);
			coConf.setReqTimeout(CServiceConstants.DEFAULT_CO_TO);
		}
		List<NotifDestination> coNotifs = ccDao.getNotifDestinations(orgId, propId, new NotifType[]{NotifType.CO_REQ, NotifType.CO_TIMEOUT_ALERT, NotifType.BILL_NOTIF});
		if (coNotifs != null) {
			Collections.sort(coNotifs, NotifDestination.getComparator());
			coConf.setReqDest(searchNotifDest(coNotifs, NotifType.CO_REQ));
			coConf.setToAlertDest(searchNotifDest(coNotifs, NotifType.CO_TIMEOUT_ALERT));
			coConf.setBillNotifDest(searchNotifDest(coNotifs, NotifType.BILL_NOTIF));
		}
		List<NotifTemplate> coTmpls = ccDao.getNotifTemplates(orgId, propId, new TemplateType[]{TemplateType.CO_AUTO_REQ, TemplateType.CO_MANU_REQ, TemplateType.CO_TIMEOUT_ALERT, TemplateType.BILL_NOTIF});
		if (coTmpls != null) {
			Collections.sort(coTmpls, NotifTemplate.getComparator());
			coConf.setReqAutoTmpl(searchNotifTmpl(coTmpls, TemplateType.CO_AUTO_REQ));
			coConf.setReqManualTmpl(searchNotifTmpl(coTmpls, TemplateType.CO_MANU_REQ));
			coConf.setToAlertTmpl(searchNotifTmpl(coTmpls, TemplateType.CO_TIMEOUT_ALERT));
			coConf.setBillNotifTmpl(searchNotifTmpl(coTmpls, TemplateType.BILL_NOTIF));
		}
		return coConf;
	}

	@Override
	public CheckInConfiguration saveCheckInConf(CheckInConfiguration conf) {
		PropertyConfiguration update;
		if (conf.getId() != null) {
			update = ccDao.getPropertyConf(conf.getId());
			update.setPropertyId(conf.getPropertyId());
		} else {
			update = new PropertyConfiguration();
			update.setOrgId(conf.getOrgId());
			update.setPropertyId(conf.getPropertyId());
		}
		update.setCiReqTimeout(conf.getReqTimeout());
		ErrorEnums err = new CServiceCommonValidators.PropertyConfValidator().validate(update, update.isNew());
		if (err != null) {
			LOGGER.debug(err);
			return null;
		}
		if (ccDao.isPropertyConfTaken(conf.getOrgId(), conf.getPropertyId(), conf.getId())) {
			LOGGER.debug(conf.getPropertyId() + " " + ErrorEnums.GeneralError.UNIQUE_CONS);
		}
		List<NotifDestination> notifDests = new ArrayList<NotifDestination>(2);
		if (conf.getReqDest() != null) {
			conf.getReqDest().setNotifTypeEnum(NotifType.CI_REQ);
			notifDests.add(conf.getReqDest());
		}
		if (conf.getToAlertDest() != null) {
			conf.getToAlertDest().setNotifTypeEnum(NotifType.CI_TIMEOUT_ALERT);
			notifDests.add(conf.getToAlertDest());
		}
		List<NotifTemplate> notifTmpls = new ArrayList<NotifTemplate>(2);
		if (conf.getReqTmpl() != null) {
			conf.getReqTmpl().setTmplTypeEnum(TemplateType.CI_REQ);
			notifTmpls.add(conf.getReqTmpl());
		}
		if (conf.getToAlertTmpl() != null) {
			conf.getToAlertTmpl().setTmplTypeEnum(TemplateType.CI_TIMEOUT_ALERT);
			notifTmpls.add(conf.getToAlertTmpl());
		}
		CServiceCommonValidators.NotifDestinationValidator notifValidator = new CServiceCommonValidators.NotifDestinationValidator();
		for (NotifDestination nd : notifDests) {
			nd.setOrgId(conf.getOrgId());
			nd.setPropertyId(conf.getPropertyId());
			err = notifValidator.validate(nd, nd.isNew());
			if (err != null) {
				LOGGER.debug(err);
				return null;
			}
			transferBaseVals(nd, nd, nd.isNew());
		}
		CServiceCommonValidators.NotifTemplateValidator tmplValidator = new CServiceCommonValidators.NotifTemplateValidator();
		for (NotifTemplate nt : notifTmpls) {
			nt.setOrgId(conf.getOrgId());
			nt.setPropertyId(conf.getPropertyId());
			err = tmplValidator.validate(nt, nt.isNew());
			if (err != null) {
				LOGGER.debug(err);
				return null;
			}
			transferBaseVals(nt, nt, nt.isNew());
		}

		transferBaseVals(update, update, update.isNew());
		ccDao.savePropertyConf(update);
		ccDao.updateNotifDestination(notifDests);
		ccDao.updateNotifTemplate(notifTmpls);

		conf.setId(update.getId());
		conf.setReqTimeout(update.getCiReqTimeout());
		return conf;
	}

	@Override
	public CheckOutConfiguration saveCheckOutConf(CheckOutConfiguration conf) {
		PropertyConfiguration update;
		if (conf.getId() != null) {
			update = ccDao.getPropertyConf(conf.getId());
		} else {
			update = new PropertyConfiguration();
			update.setOrgId(conf.getOrgId());
			update.setPropertyId(conf.getPropertyId());
		}
		update.setCoReqTimeout(conf.getReqTimeout());
		update.setCoSucMsg(conf.getSucMsg());
		update.setCoTandc(conf.getTandc());
		update.setCoTandcSecLang(conf.getTandcSecLang());
		ErrorEnums err = new CServiceCommonValidators.PropertyConfValidator().validate(update, update.isNew());
		if (err != null) {
			LOGGER.debug(err);
			return null;
		}
		if (ccDao.isPropertyConfTaken(conf.getOrgId(), conf.getPropertyId(), conf.getId())) {
			LOGGER.debug(conf.getPropertyId() + " " + ErrorEnums.GeneralError.UNIQUE_CONS);
		}

		List<NotifDestination> notifDests = new ArrayList<NotifDestination>(2);
		if (conf.getReqDest() != null) {
			conf.getReqDest().setNotifTypeEnum(NotifType.CO_REQ);
			notifDests.add(conf.getReqDest());
		}
		if (conf.getToAlertDest() != null) {
			conf.getToAlertDest().setNotifTypeEnum(NotifType.CO_TIMEOUT_ALERT);
			notifDests.add(conf.getToAlertDest());
		}
		if (conf.getBillNotifDest() != null) {
			conf.getBillNotifDest().setNotifTypeEnum(NotifType.BILL_NOTIF);
			notifDests.add(conf.getBillNotifDest());
		}
		List<NotifTemplate> notifTmpls = new ArrayList<NotifTemplate>(2);
		if (conf.getReqAutoTmpl() != null) {
			conf.getReqAutoTmpl().setTmplTypeEnum(TemplateType.CO_AUTO_REQ);
			notifTmpls.add(conf.getReqAutoTmpl());
		}
		if (conf.getReqManualTmpl() != null) {
			conf.getReqManualTmpl().setTmplTypeEnum(TemplateType.CO_MANU_REQ);
			notifTmpls.add(conf.getReqManualTmpl());
		}
		if (conf.getToAlertTmpl() != null) {
			conf.getToAlertTmpl().setTmplTypeEnum(TemplateType.CO_TIMEOUT_ALERT);
			notifTmpls.add(conf.getToAlertTmpl());
		}
		if (conf.getBillNotifTmpl() != null) {
			conf.getBillNotifTmpl().setTmplTypeEnum(TemplateType.BILL_NOTIF);
			notifTmpls.add(conf.getBillNotifTmpl());
		}
		CServiceCommonValidators.NotifDestinationValidator notifValidator = new CServiceCommonValidators.NotifDestinationValidator();
		for (NotifDestination nd : notifDests) {
			nd.setOrgId(conf.getOrgId());
			nd.setPropertyId(conf.getPropertyId());
			err = notifValidator.validate(nd, nd.isNew());
			if (err != null) {
				LOGGER.debug(err);
				return null;
			}
			transferBaseVals(nd, nd, nd.isNew());
		}
		CServiceCommonValidators.NotifTemplateValidator tmplValidator = new CServiceCommonValidators.NotifTemplateValidator();
		for (NotifTemplate nt : notifTmpls) {
			nt.setOrgId(conf.getOrgId());
			nt.setPropertyId(conf.getPropertyId());
			err = tmplValidator.validate(nt, nt.isNew());
			if (err != null) {
				LOGGER.debug(err);
				return null;
			}
			transferBaseVals(nt, nt, nt.isNew());
		}

		transferBaseVals(update, update, update.isNew());
		ccDao.savePropertyConf(update);
		ccDao.updateNotifDestination(notifDests);
		ccDao.updateNotifTemplate(notifTmpls);

		conf.setId(update.getId());
		conf.setReqTimeout(update.getCoReqTimeout());
		conf.setSucMsg(update.getCoSucMsg());
		conf.setTandc(update.getCoTandc());
		conf.setTandcSecLang(update.getCoTandcSecLang());
		return conf;
	}

	@Override
	public NotifEntityIntf getRsvNotifEntity(final ReservationView rv) {
		return new NotifEntityIntf() {
//			@Override
//			public long getMsgId() {
//				return rv.getReservationViewId();
//			}

			@Override
			public String getTokenValue(String token) {
				RsvReqMsgEnum en = RsvReqMsgEnum.valueOf(token);
				switch (en) {
				case FIRST_NAME: return rv.getGuestFirstName();
				case LAST_NAME: return rv.getGuestLastName();
				case CONF_NO: return rv.getConfirmationNo();
				case ROOM_NO: return rv.getRoomNumber();
				default: return null;
				}
			}

			@Override
			public List<NotifEntityIntf> getIterTokens(String token) {
				return null;
			}
		};
	}
}
